//////////////////////////////////////////////////////////////////////
//
//  CryEngine Source code
//	
//	File:Skeleton.cpp
//  Implementation of Skeleton class (Forward Kinematics)
//
//	History:
//	January 12, 2005: Created by Ivo Herzeg <ivo@crytek.de>
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <I3DEngine.h>
#include <IRenderAuxGeom.h>
#include <IVisualLog.h>
#include "CharacterInstance.h"
#include "Model.h"
#include "ModelSkeleton.h"
#include "CharacterManager.h"
#include <float.h>
#include "LMG.h"

// Function used to display animation flags as text
const char * AnimFlags( uint32 flags )
{
	static char result[33];

	/*
		+ ???
		y	???
		b	CA_REMOVE_FROM_FIFO					=0x4000
		s	CA_MOVE2IDLE								=0x2000
		W	CA_SUPPRESS_WARNINGS        =0x1000
		V	CA_TRACK_VIEW_EXCLUSIVE     =0x0800
		F	CA_FORCE_SKELETON_UPDATE    =0x0400
		x	CA_DISABLE_MULTILAYER       =0x0200
		U	CA_PARTIAL_SKELETON_UPDATE	=0x0100
		n	CA_ALLOW_ANIM_RESTART				=0x0080
		I	CA_IDLE2MOVE								=0x0040
		A	CA_START_AFTER	  					=0x0020
		K	CA_START_AT_KEYTIME					=0x0010
		T	CA_TRANSITION_TIMEWARPING		=0x0008
		R	CA_REPEAT_LAST_KEY					=0x0004
		L	CA_LOOP_ANIMATION						=0x0002
		M	CA_MANUAL_UPDATE						=0x0001
	*/

	const char codes[] = "+ybsWVFxUnIAKTRLM";
	for ( int i = 0; result[i] = codes[i]; ++i )
		if ( (flags & (1<<(sizeof(codes)-i-2))) == 0 )
			result[i] = '-';
	return result;
}

SPU_NO_INLINE uint32 CheckIsCAFLoaded(CAnimationSet *pAnimationSet, int32 nAnimID)
{
	const ModelAnimationHeader* pAnimCAF = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );
	if (pAnimCAF->m_nAssetType==CAF_File)
	{
		int32 nGlobalID = pAnimationSet->GetGlobalIDByAnimID_Fast( nAnimID );
		GlobalAnimationHeaderCAF& rGAHeader = g_AnimationManager.m_arrGlobalCAF[nGlobalID];
		rGAHeader.ConnectCAFandDBA();
		uint32 loaded = rGAHeader.IsAssetLoaded();
		if (!loaded)
		{
			rGAHeader.StartStreamingCAF();
		}

		return (loaded!=0);
	}

	if (pAnimCAF->m_nAssetType==AIM_File)
		return 1;

	return 0;
}

/////////////////////////////////////////////////////////////////////////////////
// this function is handling animation-update, interpolation and transitions   //
/////////////////////////////////////////////////////////////////////////////////
SPU_NO_INLINE uint32 CSkeletonAnim::BlendManager(	f32 fDeltaTime,	DynArray<CAnimation>& arrAFIFO, uint32 nLayer )
{
	DEFINE_PROFILER_FUNCTION();

	uint32 NumAnimsInQueue = arrAFIFO.size();
	assert(NumAnimsInQueue > 0);
	assert(nLayer < numVIRTUALLAYERS);

	CAnimation *arrAnimFiFo = SPU_PTR_SELECT( &arrAFIFO[0], (nLayer == 0 ? gAninmationFifoLayer0 : gAninmationFifo) );

	//the first animation in the queue MUST be activated
	if (arrAnimFiFo[0].m_bActivated==0)
	{ 
		int32 allLoaded = 1;
		CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
		if (arrAnimFiFo[0].m_Parametric == NULL)
		{
			allLoaded = CheckIsCAFLoaded(pAnimationSet, arrAnimFiFo[0].m_nAnimID);
		}
		else
		{
			uint32 counter=0;
			uint32 num = arrAnimFiFo[0].m_Parametric->m_numAnims;
			for (uint32 a=0; a<num; a++)
			{
				int32 nAnimID = arrAnimFiFo[0].m_Parametric->m_nAnimID[a];
				allLoaded &= CheckIsCAFLoaded(pAnimationSet, nAnimID);
			}

			const ModelAnimationHeader* pAnimLMG = pAnimationSet->GetModelAnimationHeader(arrAnimFiFo[0].m_nAnimID);
			if (pAnimLMG->m_nAssetType==LMG_File)
			{
				GlobalAnimationHeaderLMG& rLMG = g_AnimationManager.m_arrGlobalLMG[pAnimLMG->m_nGlobalAnimId];
				uint32 numSpliceAnims = rLMG.m_strSpliceAnim.size();
				for (uint32 i=0; i<numSpliceAnims; i++)
				{
					int32 nAnimIDNS	= pAnimationSet->GetAnimIDByCRC(rLMG.m_strSpliceAnim[i].m_CRC32);
					if (nAnimIDNS >= 0)
					{
						allLoaded &= CheckIsCAFLoaded(pAnimationSet, nAnimIDNS);
					}
				}
			}
		}

		arrAnimFiFo[0].m_bActivated = allLoaded;
	}

	if (arrAnimFiFo[0].m_bActivated==0)
		return 0;


	//	uint32 nMaxActiveInQueue=2;	
	uint32 nMaxActiveInQueue=MAX_EXEC_QUEUE;	

	if (nMaxActiveInQueue>NumAnimsInQueue)
		nMaxActiveInQueue=NumAnimsInQueue;

	if (NumAnimsInQueue>nMaxActiveInQueue)
		NumAnimsInQueue=nMaxActiveInQueue;


	uint32 aq = EvaluateTransitionFlags( arrAnimFiFo, NumAnimsInQueue );	

	//-----------------------------------------------------------------------------------------------
	//-----------------------------------------------------------------------------------------------
	//-----------------------------------------------------------------------------------------------

	if (aq>nMaxActiveInQueue)
		aq=nMaxActiveInQueue;

	nMaxActiveInQueue=aq;

#ifdef _DEBUG
	for (uint32 a=0; a<nMaxActiveInQueue; a++)
	{
		assert(arrAnimFiFo[a].m_bActivated);
		f32 t=arrAnimFiFo[a].m_fAnimTime;
		assert(t>=0.0f && t<=1.0f);  //most likely this is coming from outside if CryAnimation
	}
#endif

	if (!m_TrackViewExclusive || (m_pInstance->m_SkeletonPose.m_LookIKLayer == nLayer))
	{
		m_layers[nLayer].m_transitionQueue.TransitionsBetweenSeveralAnimations( nMaxActiveInQueue );
		m_layers[nLayer].m_transitionQueue.UpdateTransitionTime( nMaxActiveInQueue, fDeltaTime, m_TrackViewExclusive, m_pInstance->m_fOriginalDeltaTime );
		m_layers[nLayer].m_transitionQueue.AdjustTransitionWeights( nMaxActiveInQueue );				
	}
	else
		m_layers[nLayer].m_transitionQueue.ApplyManualMixingWeight( nMaxActiveInQueue );

#ifdef _DEBUG
	for (uint32 j=0; j<nMaxActiveInQueue; j++)
	{
		if (arrAnimFiFo[j].m_fTransitionWeight<0.0f)
			assert(0);
		if (arrAnimFiFo[j].m_fTransitionWeight>1.0f)
			assert(0);
		if (arrAnimFiFo[j].m_fTransitionPriority<0.0f)
			assert(0);
		if (arrAnimFiFo[j].m_fTransitionPriority>1.0f)
			assert(0);
	}
#endif

	f32 TotalWeights=0.0f;
	for (uint32 i=0; i<nMaxActiveInQueue; i++) 
		TotalWeights += arrAnimFiFo[i].m_fTransitionWeight;
	//		assert( fabsf(TotalWeights-1.0f) < 0.001f );

	f32 fTotal = fabsf(TotalWeights-1.0f);
	if (fTotal>0.01f)
	{
		assert(!"sum of transition-weights is wrong");	
		CryFatalError("CryAnimation: sum of transition-weights is wrong: %f %s",fTotal,m_pInstance->m_pModel->GetModelFilePath());
	}

	for (uint32 a=0; a<nMaxActiveInQueue; a++)
	{
		CAnimation &rCurAnim = arrAnimFiFo[a];
		assert(rCurAnim.m_bActivated);
		if (rCurAnim.m_AnimParams.m_nFlags & CA_FORCE_SKELETON_UPDATE)
		{
			m_pSkeletonPose->m_bFullSkeletonUpdate=1; //This means we have to do a full-skeleton update
		//	float fColor[4] = {1,1,0,1};
		//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.7f, fColor, false,"CA_FORCE_SKELETON_UPDATE" );	
		//	g_YLine+=16.0f;
		}
	}
	UpdateParameters( arrAnimFiFo, nMaxActiveInQueue, nLayer, fDeltaTime );
	m_layers[nLayer].m_transitionQueue.AdjustAnimationTimeForTimeWarpedAnimations( nMaxActiveInQueue );	
	UpdateAnimTimeAndExecBuffer( arrAnimFiFo, nMaxActiveInQueue, nLayer, NumAnimsInQueue );

	return 0;
}



/////////////////////////////////////////////////////////////////////////////////
// this function is handling animation-update, interpolation and transitions   //
/////////////////////////////////////////////////////////////////////////////////
uint32 CSkeletonAnim::BlendManagerDebug(	DynArray<CAnimation>& arrAFIFO, uint32 nVLayer )
{
	f32 fColor[4] = {1,0,0,1};

	if (Console::GetInst().ca_DebugTextLayer != 0xffffffff 
		&& nVLayer != Console::GetInst().ca_DebugTextLayer)
		return 0;
	
	if (Console::GetInst().ca_DebugTextTarget.empty() == false
		&& strstr(m_pInstance->GetFilePath(), Console::GetInst().ca_DebugTextTarget.c_str()) == NULL)
		return 0;

	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;

	uint32 NumAnimsInQueue = arrAFIFO.size();
	for (uint32 i=0; i<NumAnimsInQueue; i++)
	{

		if (arrAFIFO[i].m_bActivated)
		{
			fColor[0] = arrAFIFO[i].m_fTransitionWeight;
			fColor[1] = (nVLayer>0);// % 2 ? arrAFIFO[i].m_fTransitionWeight * 0.3f : 0.0f;
			fColor[2] = 0.0f;
			fColor[3] = arrAFIFO[i].m_fTransitionWeight;
			if (nVLayer > 0 && nVLayer < 16)
				fColor[3]=m_layers[nVLayer].m_transitionQueue.m_blending*m_layers[nVLayer].m_transitionQueue.m_blendingMultiplier*arrAFIFO[i].m_fTransitionWeight;
		}
		else
		{
			fColor[0] = 0.0f;
			fColor[1] = 0.0f;
			fColor[2] = 0.0f;
			fColor[3] = 0.5f;
		}

		fColor[3]=(fColor[3]+1)*0.5f;
		const char* pAnimName=pAnimationSet->GetModelAnimationHeaderRef(arrAFIFO[i].m_nAnimID).GetAnimName();

		g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.2f, fColor, false,"AnimInAFIFO %02d: t:%d  %s  ATime:%.2f (%.2fs/%.2fs) ASpd:%.2f Flag:%08x (%s) TTime:%.2f TWght:%.2f seg:%02d inmem:%i", nVLayer, arrAFIFO[i].m_AnimParams.m_nUserToken,  pAnimName, arrAFIFO[i].m_fAnimTime, arrAFIFO[i].m_fAnimTime*arrAFIFO[i].m_fCurrentDuration, arrAFIFO[i].m_fCurrentDuration, arrAFIFO[i].m_fCurrentPlaybackSpeed, arrAFIFO[i].m_AnimParams.m_nFlags, AnimFlags(arrAFIFO[i].m_AnimParams.m_nFlags), arrAFIFO[i].m_AnimParams.m_fTransTime, arrAFIFO[i].m_fTransitionWeight, arrAFIFO[i].m_nSegCounter, IsAnimationInMemory(pAnimationSet, &arrAFIFO[i]) ); g_YLine+=0x0e;
		if (nVLayer == 0)
		{
			const char* pAimAnimName0=0;
			if (arrAFIFO[i].m_nAnimDirID0>=0)
				pAimAnimName0=pAnimationSet->GetModelAnimationHeaderRef(arrAFIFO[i].m_nAnimDirID0).GetAnimName();
			const char* pAimAnimName1=0;
			if (arrAFIFO[i].m_nAnimDirID1>=0)
				pAimAnimName1=pAnimationSet->GetModelAnimationHeaderRef(arrAFIFO[i].m_nAnimDirID1).GetAnimName();

			g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.2f, fColor, false,"AimPoses: %s %s  (Try)UseAimIK: %d  AimIKBlend: %.2f  SetAimIKBlend: %.2f   AimIKInfluence: %.2f",pAimAnimName0,pAimAnimName1,m_pSkeletonPose->GetPoseBlenderAim().m_blender.m_UseDirIK,m_pSkeletonPose->GetPoseBlenderAim().m_blender.m_fDirIKBlend,m_pSkeletonPose->GetPoseBlenderAim().m_blender.m_Set_fDirIKBlend,m_pSkeletonPose->GetPoseBlenderAim().m_blender.m_fDirIKInfluence );g_YLine+=0x0e;
			if (arrAFIFO[i].m_Parametric)
			{
				g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.2f, fColor, false,"speed: %+.2f   turn: %+.2f   slope: %+.2f   strafe:(%+.2f %+.2f)", arrAFIFO[i].m_Parametric->m_BlendSpace.m_speed, arrAFIFO[i].m_Parametric->m_BlendSpace.m_turn, arrAFIFO[i].m_Parametric->m_BlendSpace.m_slope,  arrAFIFO[i].m_Parametric->m_BlendSpace.m_strafe.x, arrAFIFO[i].m_Parametric->m_BlendSpace.m_strafe.y );g_YLine+=0x0e;
			}
		}
		else
		{
			assert(nVLayer < 16);
			g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.2f, fColor, false,"Additive: %.2f", m_layers[nVLayer].m_transitionQueue.m_additiveWeights); g_YLine+=0x0e;
		}
		g_YLine+=0x02;

	}
	//if (NumAnimsInQueue > 0)
	//	g_YLine+=0x0e;

	return 0;
}






/////////////////////////////////////////////////////////////////////////////////
//             this function is handling blending between layers               //
/////////////////////////////////////////////////////////////////////////////////
void CSkeletonAnim::LayerBlendManager( f32 fDeltaTime, uint32 nLayer )
{
	if (nLayer==0)
		return;


	int8 status = GetActiveLayer(nLayer);

	CTransitionQueue& transitionQueue = m_layers[nLayer].m_transitionQueue;

	f32 time = transitionQueue.m_blendingTime;

	f32 fCurLayerBlending = transitionQueue.m_blending;
	if (time < 0)
	{
		fCurLayerBlending = 1.0f;
	}
	else
	{
		if (fDeltaTime == 0.0f)
			fDeltaTime = m_pInstance->m_fOriginalDeltaTime;
	}

	if (time<0.00001f)
		time=0.00001f;

	//f32 fColor[4] = {0.5f,0.0f,0.5f,1};
	//g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"nLayer: %02d  status: %1d, blend: %f  ", nLayer, status, fCurLayerBlending[nLayer]  ); 
	//g_YLine+=0x10;
	if (status)
		fCurLayerBlending += fDeltaTime/time;
	else
		fCurLayerBlending -= fDeltaTime/time;

	if (fCurLayerBlending > 1.0f)
		fCurLayerBlending = 1.0f;
	if (fCurLayerBlending < 0.0f)
		fCurLayerBlending = 0.0f;


	uint32 numAnims = transitionQueue.m_animations.size();

	CRY_ASSERT(!status || numAnims > 0);

	if (numAnims && !status && (fCurLayerBlending==0.0f) )
	{			
			transitionQueue.m_animations[0].m_bRemoveFromQueue=1;
	}

	transitionQueue.m_blending = fCurLayerBlending;
	return;
}






//---------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------

SPU_NO_INLINE void CSkeletonAnim::UpdateAndPushIntoExecBuffer( CAnimation *arrAnimFiFo, uint32 nLayer, uint32 NumAnimsInQueue, uint32 AnimNo )
{
	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;

	CAnimation &rAnimation = arrAnimFiFo[AnimNo];

	assert(rAnimation.m_fAnimTime<=2.0f);

	rAnimation.m_bEndOfCycle=0;
	uint32 numAimPoses=0;
	numAimPoses+=(rAnimation.m_nAnimDirID0>=0);
	numAimPoses+=(rAnimation.m_nAnimDirID1>=0);
	uint32 ManualUpdate			= (rAnimation.m_AnimParams.m_nFlags & CA_MANUAL_UPDATE)!=0;
	uint32 LoopAnimation			= (rAnimation.m_AnimParams.m_nFlags & CA_LOOP_ANIMATION)!=0;
	uint32 RepeatLastKey			= (rAnimation.m_AnimParams.m_nFlags & CA_REPEAT_LAST_KEY)!=0;
	if (LoopAnimation)	RepeatLastKey=0;
	if (ManualUpdate) rAnimation.m_fCurrentDeltaTime=0.0f;

	if (rAnimation.m_fCurrentDeltaTime>0.9f)	rAnimation.m_fCurrentDeltaTime=0.9f;
	if (rAnimation.m_fCurrentDeltaTime<-0.9f) rAnimation.m_fCurrentDeltaTime=-0.9f;


	f32 fCurrentDeltaTime = rAnimation.m_fCurrentDeltaTime;
	rAnimation.m_fAnimTimePrev	=	rAnimation.m_fAnimTime;
	rAnimation.m_fAnimTime		 +=	fCurrentDeltaTime;
	assert(rAnimation.m_fAnimTime<=2.0f);

	if (rAnimation.m_fAnimTime>=1.9999f)
		rAnimation.m_fAnimTime=1.9999f; //this is not supposed to happen

	if (fCurrentDeltaTime>=0.0f)
	{
		//time is running forward
		if (rAnimation.m_fAnimTime>=1.0f)
		{
			rAnimation.m_nSegCounter++;

			uint32 Loop   =  rAnimation.m_nSegCounter<rAnimation.m_nSegHighest || LoopAnimation;
			uint32 Repeat = (RepeatLastKey && rAnimation.m_nSegCounter>=rAnimation.m_nSegHighest);

			if (Loop==0)
			{
				rAnimation.m_bRemoveFromQueue=1;
				rAnimation.m_fAnimTime = 1.0f;	//anim is not looping
			}

			if (Repeat)
			{
				rAnimation.m_nRepeatCount=1;
				rAnimation.m_bRemoveFromQueue=0;
				rAnimation.m_fAnimTime = 1.0f;

				//just for multi-layer 
				uint32 flags = rAnimation.m_AnimParams.m_nFlags;
				uint32  la	= flags&CA_LOOP_ANIMATION;
				if (NumAnimsInQueue==1 && nLayer && la==0)
				{
					uint32  rlk = flags&CA_REPEAT_LAST_KEY;
					uint32  fo	= flags&CA_FADEOUT;
					if (rlk && fo)
					{
						// to move a lot of unecessary stl main memory operations away from spu, call StopAnimationLayer deffered
						// for this re just remember the layer to stop the animation in a qeue, which is checked after the job has finished
						m_arrStopAnimationQueue[nStopAnimationQueuePos++] = nLayer;
					}

				}
			}

			if (Loop==1 && Repeat==0)
			{
				rAnimation.m_fAnimTime = 0.0f;
				assert(rAnimation.m_fAnimTime>=0.0f && rAnimation.m_fAnimTime<=1.0f);

				rAnimation.m_nLoopCount++;

				if (rAnimation.m_Parametric == NULL)
				{
					const int16 segCounter = rAnimation.m_nSegmentCounter+1;
					rAnimation.m_nSegmentCounter = segCounter;
					if (segCounter >= rAnimation.m_nSegHighest)
					{
						rAnimation.m_nSegmentCounter = 0;
					}
				}
				else
				{
					uint32 numAnims0 = rAnimation.m_Parametric->m_numAnims;
					for (uint32 i=0; i<numAnims0; i++)
					{
						int32 nAnimID = rAnimation.m_Parametric->m_nAnimID[i];
						const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );
						if (pAnim->m_nAssetType==CAF_File)
						{
							int32 GlobalID = pAnim->m_nGlobalAnimId;
							GlobalAnimationHeaderCAF& rGAH = g_AnimationManager.m_arrGlobalCAF[ GlobalID ];
							int32 seg = rGAH.m_Segments-1;
							const int16 segCounter = rAnimation.m_Parametric->m_nSegmentCounter[i]+1;
							rAnimation.m_Parametric->m_nSegmentCounter[i] = segCounter;
							if (segCounter>seg)
								rAnimation.m_Parametric->m_nSegmentCounter[i]=0;
						}
					}
				}

				rAnimation.m_bEndOfCycle=1;
			}	
		}
	}
	else
	{
		//time is running backwards
		if (rAnimation.m_fAnimTime<=0.0f)
		{
			if (LoopAnimation==0)
			{
				rAnimation.m_bRemoveFromQueue=1;
				rAnimation.m_fAnimTime = 0.0f;
			}
			if (RepeatLastKey)
			{
				rAnimation.m_bRemoveFromQueue=0;
				rAnimation.m_nRepeatCount=1;
				rAnimation.m_fAnimTime = 0.0f;

				//just for multi-layer 
				uint32 flags = rAnimation.m_AnimParams.m_nFlags;
				uint32  la	= flags&CA_LOOP_ANIMATION;
				if (NumAnimsInQueue==1 && nLayer && la==0)
				{
						uint32  rlk = flags&CA_REPEAT_LAST_KEY;
						uint32  fo	= flags&CA_FADEOUT;
						if (rlk && fo)
						{
								// to move a lot of unecessary stl main memory operations away from spu, call StopAnimationLayer deffered
								// for this re just remember the layer to stop the animation in a qeue, which is checked after the job has finished
								m_arrStopAnimationQueue[nStopAnimationQueuePos++] = nLayer;
						}

				}
			}
			if (LoopAnimation && RepeatLastKey==0)
			{
				rAnimation.m_fAnimTime=1.0f;
				rAnimation.m_nLoopCount++;
				rAnimation.m_bEndOfCycle=1;
			}	
		}
	}
	if (rAnimation.m_fTransitionWeight == 0.0f)
	{
		if(!m_TrackViewExclusive || m_pInstance->m_SkeletonPose.m_LookIKLayer == nLayer)
		{
			rAnimation.m_bRemoveFromQueue = 1;
		}
	}

	assert(rAnimation.m_fAnimTime>=0.0f && rAnimation.m_fAnimTime<=1.0f);



	return;
}


//----------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------

SPU_NO_INLINE void CSkeletonAnim::AnimCallback ( CAnimation* arrAFIFO, uint32 AnimNo, uint32 AnimQueueLen)
{
	CAnimation &rAnimation = arrAFIFO[AnimNo];

	const int32 nEOC = rAnimation.m_bEndOfCycle;
	f32 timeold=rAnimation.m_fAnimTimePrev;
	f32 timenew=rAnimation.m_fAnimTime;

	// prevent sending events when animation is not playing
	// like when there's an event at time 1.0f and repeat-last-key flag is used
	// otherwise it may happen to get the event each frame
	if ( timeold == timenew )
		return;
		
	if ( fabsf(m_pInstance->m_fDeltaTime) <0.00001f)
		return;
		
	const bool playingForward = (rAnimation.m_fCurrentPlaybackSpeed > 0.0f);
		
	if (nEOC)
	{
		timeold = playingForward ? 0.0f : 1.0f;
	}
	
	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;

	uint32 numEvents=0;
	AnimEvents* pCurAnimEvent=0;
	int32 nAnimID=-1;
	const char* pFilePath=0;
	if (rAnimation.m_Parametric == NULL)
	{
		const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(rAnimation.m_nAnimID) );
		if (pAnim->m_nAssetType==CAF_File)
		{
			int32 nGlobalLMGID	=	pAnim->m_nGlobalAnimId;
			nAnimID							= rAnimation.m_nAnimID;
			GlobalAnimationHeaderCAF& rGlobalAnimHeaderCAF = g_AnimationManager.m_arrGlobalCAF[nGlobalLMGID];
			pFilePath = rGlobalAnimHeaderCAF.GetFilePath();
			numEvents = rGlobalAnimHeaderCAF.m_AnimEventsCAF.size();
			if (numEvents==0)
				return;
			pCurAnimEvent = &rGlobalAnimHeaderCAF.m_AnimEventsCAF[0];
		}
	}
	else
	{
		assert(rAnimation.m_nAnimID>=0);
		const ModelAnimationHeader* pAnim = pAnimationSet->GetModelAnimationHeader(rAnimation.m_nAnimID);

		if(pAnim->m_nAssetType==LMG_File)
		{
			GlobalAnimationHeaderLMG& rGlobalAnimHeaderLMG = g_AnimationManager.m_arrGlobalLMG[pAnim->m_nGlobalAnimId];
			pFilePath = rGlobalAnimHeaderLMG.GetFilePath();
			numEvents = rGlobalAnimHeaderLMG.m_AnimEventsLMG.size();
			if (numEvents==0)
				return;
			pCurAnimEvent = &rGlobalAnimHeaderLMG.m_AnimEventsLMG[0];
		}
	}

	if (pCurAnimEvent)
	{		
		for (uint32 i=0; i<numEvents; i++)
		{
			const f32 time = pCurAnimEvent[i].m_time;	
			const bool forwardCondition = (playingForward && (time < timenew) && (time >= timeold));
			const bool backwardCondition = (!playingForward && (time > timenew) && (time <= timeold));
			
			if(forwardCondition || backwardCondition)
			{
				int32 a,b;

				const char* strModelName = pCurAnimEvent[i].m_strModelName.c_str();
				const char* strFilePath = SPU_MAIN_PTR( m_pInstance->CCharInstance::GetFilePath() );

				for (a=0; a<256; a++)	if (strModelName[a]==0)	break;
				for (b=0; b<256; b++) if (strFilePath[b]==0) break;
				uint32 equal=1;
				if (a)
				{
					for (; a>-1; a--,b--)
					{
						uint32 c0=strModelName[a];
						uint32 c1=strFilePath[b];
						if (c0!=c1)	{	equal=0; break;	}
					}
				}

				if (equal)
				{
					if( m_nEventQueuePos >= 5 )
					{
						//AnimWarning("SkeletonAnimEventQueue full, discarding new Events for this frame");
					}
					else
					{
						AnimEventInstance &rCurEventInstance = spuEventQueue[m_nEventQueuePos++];		
						rCurEventInstance.m_time								= time; 
						rCurEventInstance.m_AnimPathName				= pFilePath; 
						rCurEventInstance.m_AnimID							= nAnimID;
						rCurEventInstance.m_nAnimNumberInQueue	= AnimQueueLen-1 - AnimNo;  
						rCurEventInstance.m_fAnimPriority				= rAnimation.m_fTransitionPriority;
						rCurEventInstance.m_EventNameLowercaseCRC32 = pCurAnimEvent[i].m_EventNameLowercaseCRC32;
						rCurEventInstance.m_EventName						= pCurAnimEvent[i].m_strEventName.c_str();
						rCurEventInstance.m_CustomParameter			= pCurAnimEvent[i].m_strCustomParameter;
						rCurEventInstance.m_BonePathName				= pCurAnimEvent[i].m_strBoneName.c_str();
						rCurEventInstance.m_vOffset							= pCurAnimEvent[i].m_vOffset;
						rCurEventInstance.m_vDir								= pCurAnimEvent[i].m_vDir;
					}	
				}
			}             
		}
	}
}


//----------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////






f32 CSkeletonAnim::GetRelRotationZ(f32 assetRot)
{
	//	float fColor1[4] = {0,1,0,1};
	//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor1, false,"assetRot%f: assetRot %f",assetRot,assetRot/m_pInstance->m_fDeltaTime );	
	//	g_YLine+=16.0f;

	if (m_CharEditBlendSpaceOverrideEnabled[eMotionParamID_TurnSpeed])
		return assetRot;

	f32 parameterizedRotBlended = 0.0f;

	int layer = 0;
	int animCount = GetNumAnimsInFIFO(layer);

	//	float weightSum = 0.0f;
	for (int animIndex = 0; animIndex < animCount; ++animIndex)
	{
		const CAnimation& anim = GetAnimFromFIFO(layer, animIndex);
		if (anim.m_bActivated)
		{
			if (anim.m_Parametric)
			{
				const MotionParamBlendSpace& turnSpeed = anim.m_Parametric->m_params[eMotionParamID_TurnSpeed].blendspace;
				const MotionParamBlendSpace& turnAngle = anim.m_Parametric->m_params[eMotionParamID_TurnAngle].blendspace;

				f32 offset	= turnSpeed.m_fProceduralOffset + turnAngle.m_fProceduralOffset;
				f32 parameterizedRot=0;
				if (offset)
					parameterizedRot = offset*m_pInstance->m_fDeltaTime;  //just for parameterized turning while running
				else
					parameterizedRot = assetRot;  //Currently used just for Idle2Move and IdleStepTurns

				parameterizedRotBlended += parameterizedRot*anim.m_fTransitionWeight; 
			}
			else
			{
				parameterizedRotBlended += assetRot*anim.m_fTransitionWeight; 
			}
		}
	}

	//	if (weightSum > 0.0f)
	//		parameterizedRotBlended /= weightSum;
	//	float fColor[4] = {1,1,1,1};
	//	g_pIRenderer->Draw2dLabel(100, g_YLine, 2.0f, fColor, false, "parameterizedRotBlended: %f ",parameterizedRotBlended); 
	//	g_YLine+=0x20;

	return parameterizedRotBlended;
};


const QuatT& CSkeletonAnim::GetRelMovement() const 
{
/*
	f32 fDT=m_pInstance->m_fDeltaTime;
	if (fDT==0)
		fDT=0.000001f;

	f32 fRelMoveSpeed		= m_pSkeletonPose->GetLocator().m_fRelMoveSpeed;
	f32 fRelTurnSpeed		= m_pSkeletonPose->GetLocator().m_fRelTurnSpeed;
	f32 fRelTravelDir		= m_pSkeletonPose->GetLocator().m_fRelTravelDir;
	f32 fRelSlope				= m_pSkeletonPose->GetLocator().m_fRelSlope;
	Vec3 VecTranslation	= m_pSkeletonPose->GetLocator().m_vRelTranslation;

	if (VecTranslation.y<0)
	{
		if (VecTranslation.x<0)
			fRelTravelDir=+gf_PI*0.5f-(fRelTravelDir-gf_PI*0.5f);
		else
			fRelTravelDir=-gf_PI*0.5f-(fRelTravelDir+gf_PI*0.5f);
	}
	float fColor1[4] = {0,1,0,1};
	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor1, false,"fRelMoveSpeed: %f  fRelTurnSpeed: %f  fRelTravelDir: %f  fRelSlope: %f",fRelMoveSpeed/fDT,fRelTurnSpeed/fDT,fRelTravelDir,fRelSlope);	
	g_YLine+=16.0f;


	const Vec3 cDir = m_RelativeMovement.q.GetColumn1();
	f32 fRealMoveSpeed	= VecTranslation.GetLength()/fDT;
	f32 fRealTurnSpeed	= Ang3::CreateRadZ( Vec3(0,1,0),cDir )/fDT;
	f32 fRealTravelDir	= Ang3::CreateRadZ(Vec3(0,1,0),VecTranslation);
	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor1, false,"fRealMoveSpeed: %f  fRealTurnSpeed: %f  fRealTravelDir: %f",fRealMoveSpeed,fRealTurnSpeed,fRealTravelDir);	
	g_YLine+=16.0f;*/

	return m_RelativeMovement;
}






//! Set the current time of the given layer, in seconds
void CSkeletonAnim::SetLayerTime (uint32 nLayer, f32 fNormalizedTime)
{
	assert(fNormalizedTime>=0.0f && fNormalizedTime<=1.0f);

	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;

	uint32 numAnims = m_layers[nLayer].m_transitionQueue.m_animations.size();
	if (numAnims==1)
	{
		CAnimation &anim = m_layers[nLayer].m_transitionQueue.m_animations[0];
		if (anim.m_Parametric == NULL)
		{
			const ModelAnimationHeader* pMAG = pAnimationSet->GetModelAnimationHeader(anim.m_nAnimID);
			assert(pMAG);
			int32 nEGlobalID = pMAG->m_nGlobalAnimId;

			if (pMAG->m_nAssetType==CAF_File)
			{
				uint32 segment=0;
				GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[ nEGlobalID ];
				for (uint32 i=0; i<4; i++)
				{
					if (fNormalizedTime>=rCAF.m_SegmentsTime[i] && fNormalizedTime<=rCAF.m_SegmentsTime[i+1])
					{							
						segment=i;
						break;
					}
				}
				anim.m_nSegmentCounter=segment;

				f32 t0	=	rCAF.m_SegmentsTime[segment+0];
				f32 t1	=	rCAF.m_SegmentsTime[segment+1];
				f32 d		=	t1-t0;
				f32 t		=	fNormalizedTime-t0;
				anim.m_fAnimTime = t/d;
			}

			if (pMAG->m_nAssetType==AIM_File)
			{
				GlobalAnimationHeaderAIM& rAIM = g_AnimationManager.m_arrGlobalAIM[ nEGlobalID ];
				anim.m_fAnimTime = fNormalizedTime;
			}


		}
		else
		{
			//parametric animation
			anim.m_fAnimTime = fNormalizedTime;
		} 

		assert(anim.m_fAnimTime>=0.0f && anim.m_fAnimTime<=1.0f);

	}

};


//! Return the current time of the given layer, in normalized time [0...1]
float CSkeletonAnim::GetLayerTime (uint32 nLayer)const
{
	if (m_layers[nLayer].m_transitionQueue.m_animations.empty())
		return 0.0f;

	return m_layers[nLayer].m_transitionQueue.m_animations[0].m_fAnimTime;
};


void CSkeletonAnim::SetAnimationDrivenMotion(uint32 ts) 
{
	m_AnimationDrivenMotion=ts;  
};
void CSkeletonAnim::SetMirrorAnimation(uint32 ts) 
{
	m_MirrorAnimation=ts;  
};


// sets the animation speed scale for layers
void CSkeletonAnim::SetLayerUpdateMultiplier(int32 nLayer, f32 fSpeed)
{
	if (nLayer>=numVIRTUALLAYERS || nLayer<0)
	{
		g_pILog->LogError ("SetLayerUpdateMultiplier() was using invalid layer id: %d", nLayer);
		return;
	}
	m_layers[nLayer].m_transitionQueue.m_speedMultiplier = fSpeed;
}

void CSkeletonAnim::SetLayerBlendMultiplier(int32 nLayer, f32 fMult)
{
	if (nLayer>=numVIRTUALLAYERS || nLayer<0)
	{
		g_pILog->LogError ("invalid layer id: %d", nLayer);
		return;
	}
	m_layers[nLayer].m_transitionQueue.m_blendingMultiplier = fMult;
}

// sets the animation speed scale for layers
void CSkeletonAnim::SetAdditiveWeight(int32 nLayer, f32 fWeight)
{
	if (nLayer>=numVIRTUALLAYERS || nLayer<0)
	{
		g_pILog->LogError ("SetAdditiveWeight() was using invalid layer id: %d", nLayer);
		return;
	}
	m_layers[nLayer].m_transitionQueue.m_additiveWeights = fWeight;
}

//---------------------------------------------------------



ILINE f32 GetYaw( const Quat& quat)
{
	Vec3 forward = quat.GetColumn1(); 
	assert( fabsf(forward.z)<0.01f );
	forward.z=0; 
	forward.Normalize();
	return -atan2f( forward.x,forward.y );
}




SPU_NO_INLINE uint32 CSkeletonAnim::EvaluateTransitionFlags( CAnimation *arrAnimFiFo, uint32 numAnims )
{
	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
	//-------------------------------------------------------------------------------
	//---                            evaluate transition flags                    ---
	//-------------------------------------------------------------------------------
	uint32 aq=0;
	for (aq=1; aq<numAnims; aq++)
	{
		CAnimation &rPrevAnimation = arrAnimFiFo[aq-1];
		CAnimation &rCurAnimation = arrAnimFiFo[aq];

		if (rCurAnimation.m_bActivated)
			continue; //an activated motion will stay activated

		uint32 IsLooping			= rPrevAnimation.m_AnimParams.m_nFlags & CA_LOOP_ANIMATION;

		uint32 StartAtKeytime		= rCurAnimation.m_AnimParams.m_nFlags & CA_START_AT_KEYTIME;
		uint32 StartAfter			= rCurAnimation.m_AnimParams.m_nFlags & CA_START_AFTER;
		uint32 Idle2Move			= rCurAnimation.m_AnimParams.m_nFlags & CA_IDLE2MOVE;
		uint32 Move2Idle			= rCurAnimation.m_AnimParams.m_nFlags & CA_MOVE2IDLE;
		uint32 InMemory				= IsAnimationInMemory(pAnimationSet, &rCurAnimation);

		if (Console::GetInst().ca_DebugAnimationStreaming && InMemory==0)
		{
			const char* pName=0;
			const ModelAnimationHeader* anim	=	&pAnimationSet->GetModelAnimationHeaderRef(rCurAnimation.m_nAnimID);

			pName=anim->GetAnimName();

			float fC1[4] = {1,1,0,1};
			g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.6f, fC1, false,"Streaming Assets: %s ",pName );	
			g_YLine+=16.0f;

			g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, m_pInstance->GetFilePath(), "CryAnimation: streaming file: %s",pName );
		}


		//can we use the "Idle2Move" flag?
		if (Idle2Move)
		{
			Idle2Move=0;  //lets be pessimistic and assume we can't use this flag at all
			assert(rCurAnimation.m_bActivated==0);

			if (rPrevAnimation.m_Parametric != NULL)
			{
			//	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
				const ModelAnimationHeader* pAnim = pAnimationSet->GetModelAnimationHeader(rPrevAnimation.m_nAnimID);
				assert(pAnim->m_nAssetType==LMG_File);

				GlobalAnimationHeaderLMG& rGlobalAnimHeader = g_AnimationManager.m_arrGlobalLMG[pAnim->m_nGlobalAnimId];
				assert(rGlobalAnimHeader.m_nBlendCodeLMG);
				uint32 bc=rGlobalAnimHeader.m_nBlendCodeLMG;

				//NOTE cyrcg cannot handle casting directly from a char string to an int
				const char *I2M1 = "I2M1";
				const char *I2M2 = "I2M2";
				const char *XIM2 = "XIM2";
				const char *I2MT = "I2MT";

				if (bc== *(uint32*)I2M1) Idle2Move=1;  //for this LMG we can use it
				if (bc== *(uint32*)I2M2) Idle2Move=1;  //for this LMG we can use it
				if (bc== *(uint32*)XIM2) Idle2Move=2;  //for this LMG we can use it
				if (bc== *(uint32*)I2MT) Idle2Move=1;  //for this LMG we can use it
			}
		}

		//can we use the "StartAfter" flag?
		if (IsLooping && StartAfter)
			StartAfter=0; //if first animation is looping, then start transition immediately to prevent a hang in the FIFO

		//-------------------------------------------------------------------------------------------------
		//-- If there are several animations in the FIFO it depends on the transition flags whether      --
		//-- we can start a transition immediately. Maybe we want to play one animation after another.   --
		//-------------------------------------------------------------------------------------------------
		uint32 TransitionDelay = StartAtKeytime+StartAfter+Idle2Move+Move2Idle+(InMemory==0);
		if (TransitionDelay)
		{
			if (StartAtKeytime)
			{
				assert(rCurAnimation.m_bActivated==0);
				f32 atnew=rPrevAnimation.m_fAnimTime;
				f32 atold=rPrevAnimation.m_fAnimTime-0.000001f;
				if (atold<rCurAnimation.m_AnimParams.m_fKeyTime  && atnew>rCurAnimation.m_AnimParams.m_fKeyTime)
					rPrevAnimation.m_nKeyPassed++;

				if (rPrevAnimation.m_nKeyPassed)
					rCurAnimation.m_bActivated=true;
			}

			if (StartAfter)
			{
				assert(rCurAnimation.m_bActivated==0);
				if (rPrevAnimation.m_nRepeatCount)
					rCurAnimation.m_bActivated=true;
			}

			if (rPrevAnimation.m_Parametric != NULL)
			{
				if (Idle2Move==1)
				{
					uint32 nSegCount=rPrevAnimation.m_Parametric->m_nSegmentCounter[0];
					Vec2 vLockedStrafeVector = rPrevAnimation.m_Parametric->m_BlendSpace.m_strafe;

					if (vLockedStrafeVector.x<0.001f)
					{
						if (nSegCount)	
							rCurAnimation.m_bActivated=true;	//move to the left
					}
					else
					{
						if (nSegCount && rPrevAnimation.m_fAnimTime>0.50f)
							rCurAnimation.m_bActivated=true;	//move to the right
					}
				}

				if (Idle2Move==2)
				{
					uint32 nSegCount=rPrevAnimation.m_Parametric->m_nSegmentCounter[0];
					f32 vLockedTurnAngle = rPrevAnimation.m_Parametric->m_BlendSpace.m_turn;

					if (vLockedTurnAngle<0)
					{
						if (nSegCount)	
							rCurAnimation.m_bActivated=true;	//move to the left
					}
					else
					{
						if (nSegCount && rPrevAnimation.m_fAnimTime>0.50f)
							rCurAnimation.m_bActivated=true;	//move to the right
					}
				}
			}

			if (Move2Idle && rCurAnimation.m_Parametric)
			{
				f32	fMoveSpeed = GetCurrentVelocity().GetLength();
				uint32 uRunStop=(fMoveSpeed>2.5f);
				assert(rCurAnimation.m_bActivated==0);

				/*
				// Old code: We wait for parts of the segment where the feet are roughly on the ground
				// before triggering the Move2Idle. This looks better, but leads to overshooting and cannot
				// easily be used when you want precise stopping.
				if (rPrevAnimation.m_fAnimTime<0.15f || rPrevAnimation.m_fAnimTime>0.95f)
					rCurAnimation.m_bActivated=true,rCurAnimation.m_Parametric->m_nStopIndex=0|uRunStop;
				if (rPrevAnimation.m_fAnimTime>0.35f && rPrevAnimation.m_fAnimTime<0.65f)
					rCurAnimation.m_bActivated=true,rCurAnimation.m_Parametric->m_nStopIndex=2|uRunStop;
				*/

				// Always activate Move2Idle; this doesn't always look so 'hot', but it
				// doesn't overshoot
				rCurAnimation.m_bActivated=true;
				if (rPrevAnimation.m_fAnimTime<0.25f || rPrevAnimation.m_fAnimTime>0.80f)
				{
					rCurAnimation.m_Parametric->m_nStopIndex=0|uRunStop;
				}
				else
				{
					rCurAnimation.m_Parametric->m_nStopIndex=2|uRunStop;
				}
			}

			if (rCurAnimation.m_bActivated==0)
				break; //all other animations in the FIFO will remain not activated
		} 
		else 
		{
			//No transition-delay flag set. Thats means we can activate the transition immediately
			rCurAnimation.m_bActivated=true;
		}
	}

	return aq;
}

SPU_NO_INLINE void CTransitionQueue::TransitionsBetweenSeveralAnimations( uint32 numAnims )
{
	//----------------------------------------------------------------------------------------
	//-------            transitions between several animations                        -------
	//----------------------------------------------------------------------------------------

	//init time-warping
	for (uint32 i=1; i<numAnims; i++)
	{
		CAnimation &rPrevAnimation = m_animations[i-1];
		CAnimation &rCurAnimation = m_animations[i];

		assert(rCurAnimation.m_bActivated);// = true;

		//the new animation determines if we use time-warping or not
		uint32 timewarp = rCurAnimation.m_AnimParams.m_nFlags & CA_TRANSITION_TIMEWARPING;
		if (timewarp)
		{
			//animations are time-warped, so we have to adjust the delta-time
			f32 tp = rCurAnimation.m_fTransitionPriority;
			if (tp==0)
			{
				if ((rPrevAnimation.m_Parametric != NULL) 
					&& (rCurAnimation.m_Parametric != NULL)
					&& (rPrevAnimation.m_nAnimID == rCurAnimation.m_nAnimID))
					*rCurAnimation.m_Parametric = *rPrevAnimation.m_Parametric;

				rCurAnimation.m_fAnimTimePrev=rPrevAnimation.m_fAnimTimePrev; //copy the prev. time from previous
				rCurAnimation.m_fAnimTime=rPrevAnimation.m_fAnimTime; //copy the time from previous
				assert(rCurAnimation.m_fAnimTime>=0.0f && rCurAnimation.m_fAnimTime<=1.0f);
				rCurAnimation.m_nSegCounter=0; //don't copy the segment from previous
			}
		}
	}
}

SPU_NO_INLINE void CTransitionQueue::UpdateTransitionTime( uint32 numAnims, f32 fDeltaTime, f32 trackViewExclusive, f32 originalDeltaTime )
{
	//-----------------------------------------------------------------------------
	//-----     update the TRANSITION-TIME of all animations in the queue     -----
	//-----------------------------------------------------------------------------
	m_animations[0].m_fTransitionPriority	= 1.0f;
	//	if (arrAnimFiFo[0].m_fTransitionPriority>1.0f)	arrAnimFiFo[0].m_fTransitionPriority=1.0f;
	for (uint32 i=1; i<numAnims; i++)
	{
		CAnimation &rCurAnimation = m_animations[i];

		f32 BlendTime   = rCurAnimation.m_AnimParams.m_fTransTime;

		if(fDeltaTime == 0.0f && BlendTime == 0.0f) // if the animation system is paused, and at the same time user pushes an animation into the queue, then we should set the priority to 1 instead of 0
		{
			rCurAnimation.m_fTransitionPriority = 1.0f;
			continue;
		}

		if (BlendTime<0) BlendTime = 1.0f; //if transition-time not specified, just put the time to 1
		if (BlendTime==0.0f) BlendTime=0.0001f; //we don't want DivByZero

		f32 ttime = fabsf(fDeltaTime)/BlendTime;
		if (trackViewExclusive)	
			ttime = originalDeltaTime/BlendTime;

		rCurAnimation.m_fTransitionPriority += ttime;  //update transition time

		if (rCurAnimation.m_fTransitionPriority>1.0f)
			rCurAnimation.m_fTransitionPriority=1.0f;

	}
}

SPU_NO_INLINE void CTransitionQueue::AdjustTransitionWeights( uint32 numAnims )
{
	//---------------------------------------------------------------------------------------
	//---  here we adjust the the TRANSITION-WEIGHTS between all animations in the queue  ---
	//----------------------------------------------------------------------------------------
	m_animations[0].m_fTransitionWeight = 1.0f; //the first in the queue will always have the highest priority
	for (uint32 i=1; i<numAnims; i++)
	{
		CAnimation &rCurAnimation = m_animations[i];

		rCurAnimation.m_fTransitionWeight = rCurAnimation.m_fTransitionPriority;
		f32 scale_previous = 1.0f - rCurAnimation.m_fTransitionWeight;

		for (uint32 j=0; j<i; j++)
			m_animations[j].m_fTransitionWeight *= scale_previous;
	}

	f32 sum=0;
	for (uint32 i=0; i<numAnims; i++)
	{
		CAnimation &rCurAnimation = m_animations[i];
		f32 w = rCurAnimation.m_fTransitionWeight;
		f32 x0 = clamp(w,0.0f,1.0f)-0.5f;
		x0 = x0/(0.5f+2.0f*x0*x0)+0.5f;
		rCurAnimation.m_fTransitionWeight=x0;
		sum+=x0;
	}

	for (uint32 i=0; i<numAnims; i++)
	{
		CAnimation &rCurAnimation = m_animations[i];
		rCurAnimation.m_fTransitionWeight/=sum;
	}
}

void CSkeletonAnim::UpdateParameters( CAnimation *arrAnimFiFo, uint32 nMaxActiveInQueue, uint32 nLayer, f32 fDeltaTime )
{
//	DEFINE_PROFILER_FUNCTION();

	if (nLayer==0)
	{
		if (m_CharEditMode==0)
			SetDesiredMotionParamsFromDesiredLocalLocation(arrAnimFiFo, nMaxActiveInQueue, fDeltaTime);

		LMGBlendWeightCalculation( arrAnimFiFo, nMaxActiveInQueue );
		UpdateMotionParamBlendSpacesForActiveMotions(fDeltaTime);
	}
	ComputeBlendWeights( arrAnimFiFo, nMaxActiveInQueue, fDeltaTime, nLayer );
}

void CSkeletonAnim::LMGBlendWeightCalculation( CAnimation *arrAnimFiFo, uint32 numAnims )
{
	//-------------------------------------------------------------------
	//--------           blend-weight calculation for LMGs         ------
	//-------------------------------------------------------------------
	for (uint32 a=0; a<numAnims; a++)
	{
		CAnimation &rCurAnim = arrAnimFiFo[a];

		assert(rCurAnim.m_bActivated);
		if (rCurAnim.m_Parametric)
		{
			UpdateMotionParamDescs(*rCurAnim.m_Parametric, rCurAnim.m_fTransitionWeight);
		}
	}
}




SPU_NO_INLINE void CSkeletonAnim::ComputeBlendWeights( CAnimation *arrAnimFiFo, uint32 numAnims, f32 fDeltaTime, uint32 nLayer )
{
	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;

	for (uint32 a=0; a<numAnims; a++)
	{
		CAnimation &rCurAnim = arrAnimFiFo[a];
		assert(rCurAnim.m_bActivated);
		if (rCurAnim.m_Parametric == NULL)
		{
			rCurAnim.m_fCurrentPlaybackSpeed = rCurAnim.m_AnimParams.m_fPlaybackSpeed;

			const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(rCurAnim.m_nAnimID) );
			if (pAnim->m_nAssetType==CAF_File)
			{
				GlobalAnimationHeaderCAF& rGAH = g_AnimationManager.m_arrGlobalCAF[pAnim->m_nGlobalAnimId];
				const int32 segcount = rCurAnim.m_nSegmentCounter;
				f32 fSegTime = rGAH.GetSegmentDuration(segcount);
				fSegTime = max(fSegTime, SECONDS_PER_TICK);
				rCurAnim.m_fCurrentDuration = fSegTime;
				rCurAnim.m_fCurrentDeltaTime = (rCurAnim.m_AnimParams.m_fPlaybackSpeed * fDeltaTime) / fSegTime;
			}
			if (pAnim->m_nAssetType==AIM_File)
			{
				GlobalAnimationHeaderAIM& rAIM = g_AnimationManager.m_arrGlobalAIM[pAnim->m_nGlobalAnimId];
				const int32 segcount = rCurAnim.m_nSegmentCounter;
				f32 fSegTime = max(rAIM.m_fTotalDuration, SECONDS_PER_TICK);
				fSegTime = max(fSegTime, SECONDS_PER_TICK);
				rCurAnim.m_fCurrentDuration = fSegTime;
				rCurAnim.m_fCurrentDeltaTime = (rCurAnim.m_AnimParams.m_fPlaybackSpeed * fDeltaTime) / fSegTime;
			}


		}
		else
		{
			if (nLayer == 0)
				rCurAnim.m_fCurrentPlaybackSpeed = rCurAnim.m_Parametric->m_params[eMotionParamID_TravelSpeed].blendspace.m_fProceduralScale;
			else
				rCurAnim.m_fCurrentPlaybackSpeed = 1.0f;

			rCurAnim.m_fCurrentPlaybackSpeed *= rCurAnim.m_AnimParams.m_fPlaybackSpeed;

			LMG::ComputeWeight(pAnimationSet, *rCurAnim.m_Parametric);
			LMG::CheckBlendWeights(pAnimationSet, *rCurAnim.m_Parametric);
			LMG::TW TW0 = LMG::GetTimewarpedDuration(pAnimationSet, *rCurAnim.m_Parametric, fDeltaTime);
			rCurAnim.m_fCurrentDuration  = max(0.0001f,TW0.m_fDuration);  //still needed??
			rCurAnim.m_fCurrentDeltaTime = TW0.m_fDeltaTime*rCurAnim.m_fCurrentPlaybackSpeed;
		}
	}
}

SPU_NO_INLINE void CTransitionQueue::AdjustAnimationTimeForTimeWarpedAnimations( uint32 numAnims )
{
	//---------------------------------------------------------------------------------------
	//---    In case animations are time-warped,  we have to adjust the ANIMATION-TIME    ---
	//---                 in relation to the TRANSITION-WEIGHTS                           ---
	//---------------------------------------------------------------------------------------	
	uint8 twflag=1;
	m_animations[0].m_bTWFlag=0;
	for (uint32 i=1; i<numAnims; i++)
	{
		CAnimation &rPrevAnimation = m_animations[i-1];
		CAnimation &rCurAnimation = m_animations[i];

		uint32 timewarp = rCurAnimation.m_AnimParams.m_nFlags & CA_TRANSITION_TIMEWARPING;

		if (timewarp)
		{
			rPrevAnimation.m_bTWFlag=twflag;
			rCurAnimation.m_bTWFlag=twflag;
		}
		else
		{
			twflag++;
			rCurAnimation.m_bTWFlag=0;
		}
	}


	f32 fTransitionDelta=0;
	f32 fTransitionWeight=0;
	uint32 start=0;
	uint32 accumented=0;
	for (uint32 i=0; i<(numAnims+1); i++)
	{
		if (i<numAnims)
		{
			CAnimation &rCurAnimation = m_animations[i];
			if (rCurAnimation.m_bTWFlag)
			{
				if (accumented==0) start=i;
				fTransitionWeight += rCurAnimation.m_fTransitionWeight;
				fTransitionDelta	+= rCurAnimation.m_fCurrentDeltaTime*rCurAnimation.m_fTransitionWeight;
				accumented++;
			}
		} 
		else 
		{
			f32 tt=0.0f;
			if (fTransitionWeight) tt=fTransitionDelta/fTransitionWeight;

			//all time-warped animation will get the same delta-time 
			for (uint32 a=start; a<(start+accumented); a++)
			{
				m_animations[a].m_fCurrentDeltaTime=tt;
			}
		}
	}

}





SPU_NO_INLINE void CSkeletonAnim::UpdateAnimTimeAndExecBuffer( CAnimation *arrAnimFiFo, uint32 numAnims, uint32 nLayer, uint32 NumAnimsInQueue )
{
	//update anim-time and push them into the exec-buffer
	for (uint32 a=0; a<numAnims; a++)
	{
		CAnimation &rCurAnimation = arrAnimFiFo[a];

		assert(rCurAnimation.m_bActivated);

		UpdateAndPushIntoExecBuffer( arrAnimFiFo, nLayer, NumAnimsInQueue,a );
		AnimCallback(arrAnimFiFo,a,numAnims);

		// Deprecated as we don't use m_fAllowLeaningSmooth anymore for turn-weight smoothing:
		//
		// f32 rad = Ang3::CreateRadZ( Vec2(0,1),rCurAnimation.m_Parametric.m_BlendSpace.m_strafe );
		// SmoothCD(rCurAnimation.m_Parametric.m_BlendSpace.m_fAllowLeaningSmooth, rCurAnimation.m_Parametric.m_BlendSpace.m_fAllowLeaningSmoothRate, m_pInstance->m_fOriginalDeltaTime, f32(fabsf(rad)<0.02f), 0.25f);
		//
		//	f32 fColor[4] = {1,1,0,1};
		//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"lmg.m_BlendSpace.m_fAllowLeaningSmooth:%f  DEG: %f",arrAFIFO[a].m_LMG0.m_BlendSpace.m_fAllowLeaningSmooth, RAD2DEG(0.01f) );	g_YLine+=0x20;

	}
}



SPU_NO_INLINE uint32 CSkeletonAnim::IsAnimationInMemory(CAnimationSet* pAnimationSet, CAnimation* pAnimation)
{
	if (pAnimation->m_Parametric == NULL)
	{
		return CheckIsCAFLoaded(pAnimationSet, pAnimation->m_nAnimID);
	}
	else
	{
		uint32 num = pAnimation->m_Parametric->m_numAnims;
		for (uint32 a=0; a<num; a++)
		{
			int32 nAnimID = pAnimation->m_Parametric->m_nAnimID[a];
			if (!CheckIsCAFLoaded(pAnimationSet, nAnimID))
			{
				//--- Early outs on first fail... correct? This will stop it triggering streaming of the other animations.
				return 0;
			}
		}

		int32 LMGID = pAnimation->m_nAnimID;
		const ModelAnimationHeader* pAnimLMG = pAnimationSet->GetModelAnimationHeader(LMGID);
		if (pAnimLMG->m_nAssetType==LMG_File)
		{
			GlobalAnimationHeaderLMG& rLMG = g_AnimationManager.m_arrGlobalLMG[pAnimLMG->m_nGlobalAnimId];
			uint32 numSpliceAnims = rLMG.m_strSpliceAnim.size();
			for (uint32 i=0; i<numSpliceAnims; i++)
			{
				int32 nAnimIDNS	= pAnimationSet->GetAnimIDByCRC(rLMG.m_strSpliceAnim[i].m_CRC32);
				if (nAnimIDNS>=0)
				{ 
					if (!CheckIsCAFLoaded(pAnimationSet, nAnimIDNS))
					{
						return 0;
					}
				}
			}
		}
	}

	return 1;
}
